#include <atlbase.h>

#include <string>
#include <algorithm>
#include <memory>

#include "ActiveX.h"
#include "Miscellaneous.h"


//#import "C:\Windows\system32\aliedit\aliedit.dll" no_namespace
//#include "aliedit.tlh"
//#include "aliedit.tli"
//#import "progid:Aliedit.EditCtrl" no_namespace

using namespace std;

#define MAX_SIBLING 24

CActiveX::CActiveX(void)
{
}


CActiveX::~CActiveX(void)
{
}

void CActiveX::DoFill(IHTMLDocument2* document) throw(...)
{
	if(!document)
		throw wstring(L"Input parameter document is null!");

	auto e = this->FindElement(document);
	auto ipao = this->GetOleInPlaceActiveObject(e);
	auto hwnd = this->FindRelativeWnd(ipao);
	this->SetText(hwnd);
}

HWND CActiveX::FindRelativeWnd(IOleInPlaceActiveObject* ipao) const throw(...)
{
	HWND hwnd = NULL;

	HRESULT hr = ipao->GetWindow(&hwnd);
	if(FAILED(hr) || !hwnd)
	{
		throw wstring(L"Cannot get HWND from IOleInPlaceActiveObject");
	}

	// Hierarchy
	auto hierarchy = CMiscellaneous::Split(_maa.find(L"hierarchy")->second, L'-');
	for(auto i = hierarchy->cbegin() + 1; i != hierarchy->cend(); ++i)
	{
		long sibling = _wtol(i->c_str());

		if(sibling > MAX_SIBLING || sibling < 0)
			throw wstring(L"Invalid hierarchy ->") + _maa.find(L"hierarchy")->second;

		HWND hChild = NULL;
		do
		{
			hChild = FindWindowExW(hwnd, hChild, NULL, NULL);
		}
		while(sibling--);
		hwnd = hChild;
	}

	return hwnd;
}

CComQIPtr<IOleInPlaceActiveObject> CActiveX::GetOleInPlaceActiveObject(IHTMLElement* e) const throw(...)
{
	CComQIPtr<IOleInPlaceActiveObject> spipao;
	CComQIPtr<IHTMLObjectElement> spoe;
	CComDispatchDriver spdisp;

	//IHTMLElement -> IHTMLObjectElement
	//IHTMLObjectElement->object -> IDispatch
	//IDispatch -> IOleInPlaceActiveObject
	spoe = e;
	spoe->get_object(&spdisp);
	spipao = spdisp;

	if(!spipao)
		throw wstring(L"Failed to get IOleInPlaceActiveObject from IHTMLElement");
		
	return spipao;
}

void CActiveX::SetText(HWND hwnd) const throw(...)
{
	const wstring& method = _maa.find(L"method")->second;
	const wstring& value = _maa.find(L"value")->second;

	if(method == L"WM_CHAR")
	{
		for_each(value.cbegin(), value.cend(), [hwnd](const wchar_t c){
			SendMessageW(hwnd, WM_CHAR, c, 0);
		});
	}
	else if(method == L"WM_SETTEXT")
	{
		SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)value.c_str());
	}
	else
	{
		throw wstring(L"Unknown method ") + method;
	}
}

//void CActiveX::InitElementFromXml(IXmlReader* pReader) throw(...)
//{
//	HRESULT hr;
//	XmlNodeType nodeType;
//	const WCHAR* pwszBuffer;
//
//	
//	//TODO:
//	CElement::InitElementFromXml(pReader);
//}

void CActiveX::BuildAvailableAttributes() throw()
{
	_maa[L"value"]		= L"";
	_maa[L"hierarchy"]	= L"0";
	_maa[L"method"]		= L"WM_CHAR";
}

void CActiveX::ValidateAttributes() const throw(...)
{
	if(_maa.find(L"value")->second == L"")
		throw wstring(L"Attribute 'value' cannot be null or empty");
}